#include <stdint.h>
#include <stdlib.h>
#include <string.h>
// #include "spi.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "esp_log.h"
#include "../../boards/GENERIC/board.h"
#include "ws2812.h"

// Include MicroPython API.
#include "py/runtime.h"

/*
import ws2812
ws2812.set_raw(a, b, c)
*/

static const char *TAG = "ws2812";

// #define WS2812_SPI_MOSI_PIN     33  //32 & 33
#define WS2812_SPI_HOST         SPI2_HOST
#define WS2812_SPI_DMA_CHAN     2

#define LED_NUM             1
#define WS2812_RESET_HEAD   1 // 100us
#define LED_DATA_BUFFER_LEN (WS2812_RESET_HEAD + 24 * LED_NUM)
#define LED_BRIGTNESS       (0x5)

typedef struct {
    uint8_t g;
    uint8_t r;
    uint8_t b;
}color_t;

typedef union _ws2812_pixel{					// 单个像素的格式
	color_t rgb;
	uint8_t data[3];
}pixel_t;

struct _led_ctx
{
    uint8_t state;
    uint8_t brightness;
    uint8_t blink;
    uint32_t blink_cnt;
    pixel_t color;
    pixel_t last_color;
    pixel_t output_color;
}led_ctx;

const color_t colors[] = {
    [COLOR_RED] = {0x00, 0x01, 0x00,},
    [COLOR_GREEN] = {0x01, 0x00, 0x00,},
    [COLOR_BLUE] = {0x00, 0x00, 0x01,},
    [COLOR_YELLOW] = {0x01, 0x01, 0x00,},
};

pixel_t pixels[LED_NUM] = {
    // {0x03, 0x00, 0x00,},
    // {0x00, 0x03, 0x00,},
    // {0x00, 0x00, 0x03,},
    // {0x0f, 0x00, 0x00,},
    // {0x00, 0x0f, 0x00,},
    // {0x00, 0x00, 0x0f,},
    // {0x0f, 0x00, 0x00,},
    // {0x00, 0x0f, 0x00,},
    // {0x00, 0x00, 0x0f,},
};

color_t colors0[] = {
    {0x03, 0x00, 0x00},
    {0x00, 0x03, 0x00},
    {0x00, 0x00, 0x03},
};

//采用8SPIbit表示法，每一个LED用24*8bit也就是24byte表示
uint8_t	ws_buffer[WS2812_RESET_HEAD + 24*LED_NUM];	// WS2812码流缓存，其中有100us长度的0数据
// uint8_t *ws_buffer_heap;
spi_device_handle_t hspi;
// struct frame_buf {
// 	union ws2812_pixel color;			// 整个屏幕使用统一颜色
// 	uint8_t pixel_brightness[LED_NUM]; 	// 每个像素亮度
// };

// #define FIVEBIT_0CODE 	0x18
// #define FIVEBIT_1CODE	0x1c
//6Mhz spi clk.
#define EIGHTBIT_0CODE	0xC0
#define EIGHTBIT_1CODE	0xf8

/**
 * 转换成ws2812缓存
 * 	有两种转换模式，
 * 		一种是5个SPI bit 表示一个ws2812bit，要求SPI发送速率为4Mhz，ws2812信号频率为800k
 * 		一种是8个SPI bit 表示一个ws2812bit，要求SPI发送速率为8Mhz，ws2812信号频率为1M
 * 	经实测，还是8bit/1M 的模式比较准确，灯带不会误识别造成乱码，
 * 	因此函数的第四个参数 推荐使用 EIGHTBIT
 */
int convert2ws2812(pixel_t *pixel_buf, uint8_t *ws_buf, uint16_t buf_len)
{
	pixel_t current_pixel;
	uint8_t *subpixel = NULL;
    ws_buf[0] = 0;
    for (uint16_t pos = 0; pos < LED_NUM; pos++) {
        // 处理当前像素点颜色
        current_pixel = pixel_buf[pos];
        // 转换每个颜色通道
        memset(ws_buf + pos * 24, 0, 24);
        for(uint16_t i = 0; i < 3; i++) {
            subpixel = ws_buf + pos * 24 + i * 8 + 0;
            // for (int bit = 0; bit < 8; ++bit) {
            //     subpixel[bit] |= ((current_pixel.data[i] & (0x80>>bit)) ? EIGHTBIT_1CODE : EIGHTBIT_0CODE);
            // }
            subpixel[0] |= ((current_pixel.data[i] & 0x80) ? EIGHTBIT_1CODE : EIGHTBIT_0CODE);
            subpixel[1] |= ((current_pixel.data[i] & 0x40) ? EIGHTBIT_1CODE : EIGHTBIT_0CODE);
            subpixel[2] |= ((current_pixel.data[i] & 0x20) ? EIGHTBIT_1CODE : EIGHTBIT_0CODE);
            subpixel[3] |= ((current_pixel.data[i] & 0x10) ? EIGHTBIT_1CODE : EIGHTBIT_0CODE);
            subpixel[4] |= ((current_pixel.data[i] & 0x08) ? EIGHTBIT_1CODE : EIGHTBIT_0CODE);
            subpixel[5] |= ((current_pixel.data[i] & 0x04) ? EIGHTBIT_1CODE : EIGHTBIT_0CODE);
            subpixel[6] |= ((current_pixel.data[i] & 0x02) ? EIGHTBIT_1CODE : EIGHTBIT_0CODE);
            subpixel[7] |= ((current_pixel.data[i] & 0x01) ? EIGHTBIT_1CODE : EIGHTBIT_0CODE);
        }
    }
	return 0;
}

void ws2812_spi_tx(uint8_t *data, uint16_t len)
{
    esp_err_t ret;
    spi_transaction_t t = {
        .length = len * 8,    //bits
        .tx_buffer = data,
        // .flags = SPI_TRANS_USE_TXDATA,
        // .tx_data = {data},
        // .user = ctx,
    };
    ret = spi_device_transmit(hspi, &t);
    ESP_ERROR_CHECK(ret);
}

void ws2812_test()
{
    for (int i=0; i<LED_NUM; ++i) {
        pixels[i].rgb.r = 0x1 & ((i+0)%3);
        pixels[i].rgb.g = 0x1 & ((i+1)%3);
        pixels[i].rgb.b = 0x1 & ((i+2)%3);
    }
	memset(ws_buffer, 0, WS2812_RESET_HEAD);			// 把前面的一段填充为0
    int cursor = 0, color = 0;
    while (1) {
        memset(&pixels, 0x00, sizeof(pixels));
		memcpy(&pixels[cursor], &colors0[color], sizeof(color_t));

        cursor++;
		if (cursor >= LED_NUM) {
			cursor = 0;
			color++;
			if (color == 3) {
				color = 0;
			}
		}

        /* 根据fbuf的内容，以及8SPIbit表示法，填充ws_buf,当然要偏移掉前面的0数据段 */
        convert2ws2812(pixels, ws_buffer + WS2812_RESET_HEAD, sizeof(ws_buffer) - WS2812_RESET_HEAD);
        /* 最后用DMA把ws_buf中的码流发送出去，完成一帧的显示 */
		// HAL_SPI_Transmit_DMA(&hspi2, ws_buffer, sizeof(ws_buffer)); //PB15
        ws2812_spi_tx(ws_buffer, sizeof(ws_buffer));
        // 下面这一行的延时可以换成别的内容，因为使用DMA+SPI时，数据发送时不占用CPU时间的。
        // HAL_Delay(200);
        vTaskDelay(200 / portTICK_PERIOD_MS);
    }
}

void led_set_color_raw(uint8_t color[3]) {
    memcpy(led_ctx.color.data, color, 3);
}

void led_set_color(color_e c) {
    memcpy(led_ctx.color.data, &colors[c], 3);
}

void led_set_state(state_e state) {
    led_ctx.state = state;
}

void led_set(color_e c, state_e state) {
    led_ctx.state = state;
    memcpy(led_ctx.color.data, &colors[c], 3);
    if (LED_STA_BLINK == state) {
        led_ctx.blink_cnt = 0xffffffff;
    }
}

void led_set_blink_event(color_e c, uint32_t blink_cnt)
{
    led_ctx.state = LED_DYNAMIC_BLINK;
    memcpy(led_ctx.last_color.data, led_ctx.color.data, 3);//store last
    memcpy(led_ctx.color.data, &colors[c], 3);   //set new color
    led_ctx.blink_cnt = blink_cnt * 2;
}

void ws2812_led_task(void *arg)
{
    // led_ctx.brightness = LED_BRIGTNESS;
    while (1) {
        //fsm
        switch (led_ctx.state) {
            case LED_STA_CONST:
            {
                led_ctx.brightness = LED_BRIGTNESS;
            }
            break;

            case LED_STA_BLINK:
            {
                static bool toggle;
                toggle = !toggle;
                led_ctx.brightness = LED_BRIGTNESS * toggle;
            }
            break;

            case LED_DYNAMIC_BLINK:
            {
                static bool toggle;
                toggle = !toggle;
                if (led_ctx.blink_cnt) {
                    led_ctx.blink_cnt--;
                    led_ctx.brightness = LED_BRIGTNESS * toggle;
                }
                else {
                    led_ctx.state = LED_STA_CONST;
                    //pop last color.
                    memcpy(led_ctx.color.data, 
                            led_ctx.last_color.data,
                            3);
                }
            }
            break;

        }

        led_ctx.output_color.data[0] = led_ctx.color.data[0] * led_ctx.brightness;
        led_ctx.output_color.data[1] = led_ctx.color.data[1] * led_ctx.brightness;
        led_ctx.output_color.data[2] = led_ctx.color.data[2] * led_ctx.brightness;
        for (int i=0; i<LED_NUM; ++i) {
            memcpy(pixels[i].data, led_ctx.output_color.data, sizeof(led_ctx.output_color));
        }
        //excute
        /* 根据fbuf的内容，以及8SPIbit表示法，填充ws_buf,当然要偏移掉前面的0数据段 */
        convert2ws2812(pixels, ws_buffer + WS2812_RESET_HEAD, sizeof(ws_buffer) - WS2812_RESET_HEAD);
        /* 最后用DMA把ws_buf中的码流发送出去，完成一帧的显示 */
        ws2812_spi_tx(ws_buffer, sizeof(ws_buffer));
        // printf("led task: sta:%d bri:%d %d %d %d\n", 
        //         led_ctx.state, 
        //         led_ctx.brightness, 
        //         led_ctx.final_color[0],
        //         led_ctx.final_color[1],
        //         led_ctx.final_color[2] );
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

void ws2812_init()
{
    led_set(COLOR_RED, LED_STA_CONST);

    esp_err_t ret;
    ESP_LOGI(TAG, "Initializing bus SPI%d...", WS2812_SPI_HOST);
    const spi_bus_config_t buscfg={
        .miso_io_num = -1,
        .mosi_io_num = WS2812_SPI_MOSI_PIN,
        .sclk_io_num = -1,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = sizeof(ws_buffer),
    };

    const spi_device_interface_config_t devcfg={
        .command_bits = 0,
        .address_bits = 0,
        .clock_speed_hz = 8000000,
        .mode = 0,          //SPI mode 0
        /*
         * The timing requirements to read the busy signal from the EEPROM cannot be easily emulated
         * by SPI transactions. We need to control CS pin by SW to check the busy signal manually.
         */
        .spics_io_num = -1,
        .queue_size = 1,
        // .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_POSITIVE_CS,
        // .pre_cb = cs_high,
        // .post_cb = cs_low,
        // .input_delay_ns = 1000,  //the EEPROM output the data half a SPI clock behind.
    };
    //Initialize the SPI bus
    ret = spi_bus_initialize(WS2812_SPI_HOST, &buscfg, WS2812_SPI_DMA_CHAN);
    ESP_ERROR_CHECK(ret);
    
    spi_bus_add_device(WS2812_SPI_HOST, &devcfg, &hspi);
    // ws_buffer_heap = heap_caps_malloc(LED_DATA_BUFFER_LEN, MALLOC_CAP_DMA);

    // xTaskCreate(ws2812_test, "led_strip", 1024, NULL, 10, NULL);
    xTaskCreate(ws2812_led_task, "led_strip", 4096, NULL, 10, NULL);
}

// This is the function which will be called from Python as cexample.add_ints(a, b).
STATIC mp_obj_t ws2812_set_raw(mp_obj_t r_obj, mp_obj_t g_obj, mp_obj_t b_obj) {
    // Extract the ints from the micropython input objects.
    int r = mp_obj_get_int(r_obj);
    int g = mp_obj_get_int(g_obj);
    int b = mp_obj_get_int(b_obj);
    
    led_ctx.color.rgb.r = r;
    led_ctx.color.rgb.g = g;
    led_ctx.color.rgb.b = b;

    // Calculate the addition and convert to MicroPython object.
    return mp_obj_new_int(0);
}
// Define a Python reference to the function above.
STATIC MP_DEFINE_CONST_FUN_OBJ_3(ws2812_set_raw_obj, ws2812_set_raw);

// Define all properties of the module.
// Table entries are key/value pairs of the attribute name (a string)
// and the MicroPython object reference.
// All identifiers and strings are written as MP_QSTR_xxx and will be
// optimized to word-sized integers by the build system (interned strings).
STATIC const mp_rom_map_elem_t ws2812_module_globals_table[] = {
    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ws2812) },
    { MP_ROM_QSTR(MP_QSTR_set_raw), MP_ROM_PTR(&ws2812_set_raw_obj) },
};
STATIC MP_DEFINE_CONST_DICT(ws2812_module_globals, ws2812_module_globals_table);

// Define module object.
const mp_obj_module_t ws2812_user_cmodule = {
    .base = { &mp_type_module },
    .globals = (mp_obj_dict_t *)&ws2812_module_globals,
};

// Register the module to make it available in Python.
// Note: the "1" in the third argument means this module is always enabled.
// This "1" can be optionally replaced with a macro like MODULE_CEXAMPLE_ENABLED
// which can then be used to conditionally enable this module.
MP_REGISTER_MODULE(MP_QSTR_ws2812, ws2812_user_cmodule, 1);
